package sound;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

public class Microphone extends Thread implements Runnable {

    private BufferedInputStream input;
    private TargetDataLine targetDataLine;
    private BufferedOutputStream output;
    private AudioFormat audioFormat;
    private SourceDataLine sourceDataLine;
    @SuppressWarnings("unused")
    private boolean transmit;
    private InputStream in;
    private OutputStream out;
    private UDPStream udp;
    private Thread captureThread;
    private Thread playThread;
    //aantal geluids pakketen dat toegelaten worden vooral het als "gesproken" wordt beschouwd ipv ruis
    private static final int VOICEACTIVATION = 70;
    //buffer van de byte array voor audio te versturen
    private static final int AUDIOBUFFER = 1024;
    public int ruiswaarde = 100;

    public Microphone(String hostname, int port) {
        udp = new UDPStream(hostname, port);
        udp.start();

        captureThread = new CaptureThread();
        playThread = new PlayThread();
        captureAudio();
        this.start();
    }

    public void begin() {
        captureThread.resume();
        System.out.println("microphone thread started");
    }

    public void end() {
        udp.w.write(new byte[1024]);
        captureThread.suspend();
        System.out.println("microphone thread stopped");
    }

    @SuppressWarnings("deprecation")
    public void captureAudio() {
        audioFormat = getAudioFormat();
        DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
        try {
            sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
            sourceDataLine.open(audioFormat);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
        sourceDataLine.start();
        input = new BufferedInputStream(in);
        output = new BufferedOutputStream(out);
        try {
            Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
            audioFormat = getAudioFormat();
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
            targetDataLine = null;
            targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
            targetDataLine.open(audioFormat);
            targetDataLine.start();
            captureThread.start();
            captureThread.suspend();
            playThread.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class CaptureThread extends Thread {

        byte tempBuffer[] = new byte[AUDIOBUFFER];

        @Override
        public void run() {
            try {
                boolean nosound = false;
                while (true) {
                    //tempBuffer inlezen van microfoon
                    @SuppressWarnings("unused")
                    int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length);

                    //voice activation
                    int geluid = 0;
                    for (int i = 0; i < tempBuffer.length; i++) {
                        //Alle audioinput dat tussen de ruiswaarde & zijn tegengestelde ligt wordt beschouwd als ruis
                        if (tempBuffer[i] >= (byte) ruiswaarde | tempBuffer[i] <= (byte) (-1 * ruiswaarde)) {
                            geluid++;
                        }
                    }
                    //System.out.println("geluid: "+geluid);
                    //geluid wegschrijven over udp
                    if (geluid > VOICEACTIVATION) {
                        //System.out.println("Talking meer dan " +VOICEACTIVATION+ " geluid in " +AUDIOBUFFER+ " bytes met ruiswaarde tussen "+ruiswaarde+" en "+(-1)*ruiswaarde);
                        nosound=false;
                        udp.w.write(tempBuffer);
                    } //lege byte sturen om echo te voorkomen?
                    else {
                        if(!nosound)
                        {
                            udp.w.write(new byte[1024]);
                            nosound=true;
                        }

                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class PlayThread extends Thread {

        byte tempBuffer[] = new byte[AUDIOBUFFER];

        @Override
        public void run() {
            try {
                while (udp.r.read(tempBuffer)) {
                    sourceDataLine.write(tempBuffer, 0, AUDIOBUFFER);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private AudioFormat getAudioFormat() {
        float sampleRate = 8000.0F;
        int sampleSizeInBits = 16;
        int channels = 1;
        boolean signed = true;
        boolean bigEndian = false;
        return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
    }
}
